// VCppEFLiteDll.cpp
// compile with: /Ehsc /LD
#include "VCppEFLiteDll.h"
#include "SQLParser.h"
#include <stdexcept>
#include <stdarg.h>
#include <sstream>
#include <math.h>


using namespace std;


namespace EFLite
{
	#pragma region Data Type Definitions 

		#pragma region Date
			
			#pragma region Private Methods

				void Date::init(int _year, int _month, int _day)
				{
					if(IsValid(_year,_month,_day)) {
							year = _year;
							month = _month;
							day = _day;
					}
					else throw new exception("Invalid date format.");
				}

				void Date::init(string _Value)
				{
					if(_Value == "NULL" || _Value == "null") {
							year = 1;
							month = 0;
							day = 0;
					}
					else {
						vector<string> parts = split(_Value, '/');
						int _year = atoi(parts[0].c_str());
						int _month = atoi(parts[1].c_str());
						int _day = atoi(parts[2].c_str());
						init(_year,_month,_day);
					}
				}

				bool Date::IsValid(int _year, int _month, int _day) {

					if(_year < 0 || _month <= 0 || _month > 12) 
						return false;

					switch(_month) {
						case 1:  // January
						case 3:  // March
						case 5:  // May
						case 7:  // July
						case 8:  // August
						case 10: // October
						case 12: // December
							if(_day > 0 && _day < 32) return true;
						case 2: // February
							if(_year % 4 == 0 && (_year % 400 == 0 || _year % 100 != 0))
								if(_day >= 1 && _day <= 29) return true;
								else return false;
							else
								if(_day >=1 && _day <= 28) return true;
								else return false;
						default:
							if(_day > 0 && _day < 31) return true;
					}

					return false;
				}
			#pragma endregion 

			#pragma region Accessor Methods

				int Date::GetYear() { return year; }
	
				int Date::GetMonth() { return month; }
	
				int Date::GetDay() { return day; }

			#pragma endregion

			#pragma region Virtual Methods
			
				bool Date::HasValue() { return !(year == 1 && month == 1 && day == 1); } // default null value for Date defined as 0001/1/1

				Types Date::GetTypeId() { return DATE; }

				string Date::ToString() {
					stringstream s;
					s << year << "/" << month << "/" << day;
					return s.str();
				}

				int Date::CompareTo(Cell* c){
					if(c->GetTypeId() != GetTypeId())
						throw new exception("Date type mismatch."); // Type mismatch
					Date* d = (Date*)c;
					if(year == d->GetYear())
						if(month == d->GetMonth())
							if(day == d->GetDay())
								return 0;
							else if(day < d->GetDay())
								return -1;
							else if(day > d->GetDay())
								return 1;
							else throw new exception("Date:day comparison failed.");
						else if(month < d->GetMonth())
							return -1;
						else if(month > d->GetMonth())
							return 1;
						else throw new exception("Date:month comparison failed.");
					else if(year < d->GetYear())
						return -1;
					else if(year > d->GetYear())
						return 1;
					else throw new exception("Date:year comparison failed.");
				}

			#pragma endregion

		#pragma endregion

		#pragma region Time

			#pragma region Private Methods

				void Time::init(int _hours, int _minutes, int _seconds){
					if(IsValid(_hours,_minutes,_seconds)) {
						hours = _hours;
						minutes = _minutes;
						seconds = _seconds;
					}
					else
						throw new exception("Invalid Time format.");
				}

				void Time::init(string _Value){
					if(_Value == "NULL" || _Value == "null") {
						hours = 0;
						minutes = 0;
						seconds = 0;
					}
					else {
						vector<string> parts = split(_Value, ':');
						int _hours = atoi(parts[0].c_str());
						int _minutes = atoi(parts[1].c_str());
						int _seconds = atoi(parts[2].c_str());
						init(_hours,_minutes,_seconds);
					}
				}

				bool Time::IsValid(int hours, int minutes, int seconds){
					if(hours > 60 || hours < 0 || minutes > 60 || minutes < 0 || seconds > 60 || seconds < 0)
						return false;
					return true;
				}

			#pragma endregion

			#pragma region Accessor Methods

				int Time::GetHours() { return hours; }

				int Time::GetMinutes() { return minutes; }

				int Time::GetSeconds() { return seconds; }
			
			#pragma endregion

			#pragma region Virtual Methods

				bool Time::HasValue() { return (hours == 0 && minutes == 0 && seconds == 0); } // default null value for Time defined as 00:00:00

				Types Time::GetTypeId() { return TIME; }

				string Time::ToString() {
					stringstream s;
					s << hours << ":" << minutes << ":" << seconds;
					return s.str();
				}

				int Time::CompareTo(Cell* c)
				{
					if(c->GetTypeId() != GetTypeId())
						throw new exception("Invalid Time Format"); // Type mismatch

					Time* t = (Time*)c;

					if(hours == t->GetHours())
						if(minutes == t->GetMinutes())
							if(seconds == t->GetSeconds())
								return 0;
							else if(seconds < t->GetSeconds())
								return -1;
							else if(seconds > t->GetSeconds())
								return 1;
							else throw new exception("Time:seconds comparison failed.");
						else if(minutes < t->GetMinutes())
							return -1;
						else if(minutes > t->GetMinutes())
							return 1;
						else throw new exception("Time:minutes comparison failed.");
					else if(hours < t->GetHours())
						return -1;
					else if(hours > t->GetHours())
						return 1;
					else throw new exception("Time:hours comparison failed.");
				}
			#pragma endregion

		#pragma endregion

		#pragma region String

			bool String::HasValue() { return (Value.length() == 0); } // Zero-length string has no value IMHO

			Types String::GetTypeId() { return VARCHAR; }

			void String::SetValue(string s) 
			{
				if(s.length() < MaxSize)
					Value = s; 
				else
					throw new exception("String length exceeds max size limit.");
			}

			string String::ToString() { return Value; }

			int String::CompareTo(Cell* c)
			{
				if(c->GetTypeId() != GetTypeId())
					throw new exception("String type mismatch."); // Type mismatch

				String* s = (String*)c;

				return Value.compare(s->ToString());
			}

		#pragma endregion

		#pragma region Int

			bool Int::HasValue() { return (Value!=NULL); }

			Types Int::GetTypeId() { return INT; }

			int Int::CompareTo(Cell* c)
			{
				if(c->GetTypeId() != GetTypeId())
					throw new exception("Int type mismatch."); // Type mismatch

				Int* i = (Int*)c;

				if(Value == i->Value) return 0;
				else if(Value < i->Value) return -1;
				else if(Value < i->Value) return 1;
				else throw new exception("Int comparison failed.");
			}

			string Int::ToString()
			{
				stringstream s;
				s << Value;
				return s.str();
			}

		#pragma endregion

		#pragma region Float

			bool Float::HasValue() { return (Value!=NULL); }

			Types Float::GetTypeId() { return FLOAT; }

			int Float::CompareTo(Cell* c)
			{
				if(c->GetTypeId() != GetTypeId())
					throw new exception("Float type mismatch."); // Type mismatch

				Float* f = (Float*)c;

				if(Value == f->Value) return 0;
				else if(Value < f->Value) return -1;
				else if(Value < f->Value) return 1;
				else throw new exception("Float comparison failed.");
			}

			string Float::ToString()
			{
				stringstream s;
				s << Value;
				return s.str();
			}

		#pragma endregion

	#pragma endregion 

	#pragma region Predicate Classes Definitions

		#pragma region Class BinPred

			#pragma region Constructors

				BP::BP(string &l, string &r){
					ls = &l;
					rs = &r;
					lc = NULL;
					rc = NULL;
				}

				BP::BP(string &l, Cell *r){
					ls = &l;
					rs = NULL;
					lc = NULL;
					rc = r;
				}

				BP::BP(Cell *l, string r){
					ls = NULL;
					rc = NULL;
					lc = l;
					rs = &r;
				}

				BP::BP(Cell *l, Cell *r){
					ls = NULL;
					rs = NULL;
					lc = l;
					rc = r;
				}
			#pragma endregion

			#pragma region Evaluate
	
				bool BinPred::Evaluate(void *Row, list<CD> *cds){
					Cell* lcell, *rcell;

					if(*ls != string("") && *rs != string("") && !lc->HasValue() && !rc->HasValue()) {
						lcell = RecoverCell(ls, Row, cds);
						rcell = RecoverCell(rs, Row, cds);
					}
					else if(*ls != string("") && *rs == string("") && !lc->HasValue() && rc->HasValue()) {
						lcell = RecoverCell(ls, Row, cds);
						rcell = rc;
					}
					else if(*ls == string("") && *rs != string("") && lc->HasValue() && !rc->HasValue()) {
						lcell = lc;
						rcell = RecoverCell(rs, Row, cds);
					}
					else if(*ls == string("") && *rs == string("") && lc->HasValue() && rc->HasValue()) {
						lcell = lc;
						rcell = rc;
					}
					else	// Throw an exception; the predicate was not correctly initialized
						throw new exception();

					if(lcell == NULL)
						// Throw an exception, the cell wasn't found
						// If this was initialized with the predicate... derp
						throw new exception();

					if(rcell == NULL)
						// Throw an exception, the cell wasn't found
						// If this was initialized with the predicate... derp
						throw new exception();

					return Compare(lcell, rcell);
				}

			#pragma endregion

		#pragma endregion

		#pragma region Virtual Override of Cell::Compare
			bool EQ::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) == 0;
				throw new exception();
			}

			bool NEQ::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) != 0;
				throw new exception();
			}

			bool GT::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) > 0;
				throw new exception();
			}

			bool GTE::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) >= 0;
				throw new exception();
			}

			bool LT::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) < 0;
				throw new exception();
			}

			bool LTE::Compare(Cell *l, Cell *r) {
				if(l->GetTypeId() == r->GetTypeId() && l->HasValue() && r->HasValue())
					return l->CompareTo(r) <= 0;
				throw new exception();
			}

			bool AND::Evaluate(void* Row, list<CD> *cds)
			{
				return l->Evaluate(Row, cds) && r->Evaluate(Row, cds);
			}

			bool OR::Evaluate(void* Row, list<CD> *cds)
			{
				return l->Evaluate(Row, cds) || r->Evaluate(Row, cds);
			}

			bool NOT::Evaluate(void* Row, list<CD> *cds)
			{
				return !(p->Evaluate(Row, cds));
			}

		#pragma endregion (Cell l, Cell r) for Types which inherit from Cell

	#pragma endregion

	#pragma region Helper Functon Definitions

		Types RecoverType(string *name, void* Row, list<CD> *cds) {
		
			list<CD>::iterator iter = cds->begin(); // Starting iterator over the CDs
			list<CD>::iterator end = cds->end(); // End iterator over the CDs

			while(iter != end) // Keep going until we reach the end or get a type
				if(iter->ToString() == *name)
					return iter->GetType();

			return (Types) NULL;
		}

		Cell* RecoverCell(string *name, void* Row, list<CD> *cds) {
		
			Types type = (Types) NULL; // Initialize our searching type to be a non-enum value 
			char *liter = (char*)Row; // Change the void* to a char* so we can sum to the position of our Cell*

			list<CD>::iterator iter = cds->begin(); // Starting iterator over the CDs
			list<CD>::iterator end = cds->end(); // End iterator over the CDs

			while(iter != end && type == NULL ) // Keep going until we reach the end or get a type
			{
				if(iter->ToString() == *name)
					type = iter->GetType();

				liter += GetTypeSize(iter->GetType());
			}

			switch(type) { // Based on the type, we return the correct type of Cell*, or NULL if we did not find the column
				case INT: return (Int*) liter;
				case FLOAT: return (Float*) liter;
				case VARCHAR: return (String*) liter;
				case DATE: return (Date*) liter;
				case TIME: return (Time*) liter;
				default: return NULL;
			}
		}

		size_t GetTypeSize(Types t) {
		
			switch(t) {
				case DATE: return sizeof(Date);
				case FLOAT: return sizeof(Float);
				case INT: return sizeof(Int);
				case TIME: return sizeof(Time);
				case VARCHAR: return sizeof(String);
				default: return NULL;
			}
		}

		size_t sizeOfCD(list<CD> Columns) {
		
			size_t totalSize=0;
			list<CD>::iterator iter = Columns.begin(); 
			list<CD>::iterator end =Columns.end();
			while(iter != end){
				totalSize += GetTypeSize(iter->GetType());
				advance(iter,1);
			}
			return totalSize;
		}

		vector<string> split(string value, char delimeter) {
		
			string token;
			vector<string> tokens;
			istringstream iss(value);
			while(getline(iss, token, delimeter))
				tokens.push_back(token);

			return tokens;
		}

	#pragma endregion

	#pragma region Data Object Definitions

		#pragma region DBContext
	
			int DBContext::Create(Entity &entity)
			{
				string name = entity.GetName();
				for each(Entity ent in Entities){
					if(ent.GetName()== name){
						throw new exception("Entity Already Exists");
						return 1;
					}
				}
				Entities.push_back(entity);
				return 0;
			}

			int DBContext::Delete(Entity &entity)
			{
				list<Entity>::iterator iter = Entities.begin();
				list<Entity>::iterator end = Entities.end();
				while(iter != end){
					if((*iter)==entity){
						list<Entity>::iterator temp = iter;
						advance(temp,1);
						Entities.erase(iter);
						delete &entity;
						iter=temp;
						return 0;	// sucessfully deleted
					}
				}
				return 1; // Entity not found. Nothing Deleted
			}


	
			int DBContext::ExecuteScalar(string query, Int *result){return 1;}
			int DBContext::ExecuteScalar(string query, Float *result){return 1;}	
			int DBContext::ExecuteScalar(string query, Date *result){return 1;}
			int DBContext::ExecuteScalar(string query, Time *result){return 1;}
			int DBContext::ExecuteScalar(string query, String *result){return 1;}
	


			int DBContext::ExecuteReader(string query, Entity *result)
			{
				SQLParser sp = SQLParser(this);
				return sp.Parse(query, result);
			}

			int DBContext::ExecuteNonQuery(string query)
			{
				SQLParser sp = SQLParser(this);
				return sp.Parse(query,NULL);
			}
	
																																																																																																			#pragma region DBContext Operators
	Entity& DBContext::operator[] (string _Name)
	{
		list<Entity>::iterator iter = Entities.begin(); 
		list<Entity>::iterator end =Entities.end();
		bool found=false;
		while(iter != end)
		{
			if((*iter).ToString()==_Name)
			{
				found=true;
				break;
			}
			advance(iter,1);
		}
		if(!found)
		{
			throw new exception("Entity not found");  
		}
		return *iter;
	}

	Entity& DBContext::operator [] (string _Names[])
	{
		//Entity tot; for each(const string s in _Names){ tot+=((*this)[s]); };
		int namesLength =1;
		list<Entity>::iterator iter;
		list<Entity>::iterator end = Entities.end();
		bool found=false;
		for(int i=0; i<namesLength;i++)
		{
			iter = Entities.begin();
			while(iter != end)
			{
				if((*iter).ToString()==_Names[i])
				{
					found=true;
					break;
				}
				if(found)break;
				advance(iter,1);
			}
		}
		if(!found) //TODO: implement exception throw
		{
			throw new exception("Entity not found");  
		}
		return *iter;
	}
	#pragma endregion DBContext Operators

			string DBContext::ToString() const {return Name;}

			DBContext::DBContext(string _Name) { Name = _Name; }

		#pragma endregion

		#pragma region Entity 

			#pragma region Entity Constructors
				
				Entity::Entity(string _Name, ColumnDefinition _ColumnDefinitions[]) {
					Name = _Name;
					Columns = list<CD> (_ColumnDefinitions, _ColumnDefinitions + sizeof(_ColumnDefinitions) / sizeof(CD));
				}
				Entity::Entity()
				{
					Name="NULL";

				}

				Entity::Entity(string _Name, list<CD> ColumnDefinitions) {
					Name = _Name;
					Columns = ColumnDefinitions;
				} 	

				Entity::Entity(const Entity& CopyEntity){
					Name=CopyEntity.GetName();
					Rows=CopyEntity.Rows;
					Columns=CopyEntity.Columns;
				}
			#pragma endregion 

			#pragma region Entity Operator Overloads

				Entity& Entity::operator+= (Entity _Entity){
					for each(void* row in _Entity.Rows){ Entity::InsertByAddr(row); }
					return *this;
				}

				bool Entity::operator ==(Entity& _Entity)	{
					return (Entity::GetName() == _Entity.GetName());
				}
		
			#pragma endregion

			#pragma region Entity Methods
			
				#pragma region Accessor Methods

					Entity* Entity::Where(Predicate *p)	{
						Entity *ent = new Entity(Name, Columns);
						list<void*>::iterator iter = Rows.begin();
						list<void*>::iterator end = Rows.end();
						void* Row = &Rows;
						while(iter != end)
						{
							Row = &iter;
							if(p->Evaluate(Row, &Columns))
							{
								ent->InsertByAddr(Row);	
							}
							advance(iter,1);
						}
						return ent;
					}

					int Entity::Count(string columnName)	//returns number of rows which have non-null values 
					{
						//Find Column of interest
						size_t colOffset=0;
						Types colType;
						list<CD>::iterator iterCD = Columns.begin();	//replaced this with RecoverType() when I realized I still needed colOffest
						list<CD>::iterator endCD = Columns.end();
						while(iterCD!=endCD)
						{
							if(iterCD->ToString()!=columnName)
							{
								colOffset += GetTypeSize(iterCD->GetType());
							}
							else{
							colType = iterCD->GetType();
							break;
							}
							advance(iterCD,1);
						}
			
						int count=0;
						list<void*>::iterator iter = Rows.begin();
						list<void*>::iterator end = Rows.end();
						//Iterate through each Row on Entity and
						while(iter!=end)
						{
							switch(colType) {	//switch on type because case is required to access HasValue() and Cell is an abstract class (no casts)
								case DATE:			
									if(((Date*)((&iter)+colOffset))->HasValue())
									{
										count++;
									}
									break;
								case FLOAT:
									if(((Float*)((&iter)+colOffset))->HasValue())
									{
										count++;
									}
									break;
								case INT:
									if(((Int*)((&iter)+colOffset))->HasValue())
									{
										count++;
									}
									break;
								case TIME:
									if(((Time*)((&iter)+colOffset))->HasValue())
									{
										count++;
									}
									break;
								case VARCHAR:
									if(((String*)((&iter)+colOffset))->HasValue())
										count++;
									break;
								default:
									cout<<"Error in insert function"<<endl;
									return 0;
									break;
							}					
							advance(iter,1);
						}
						return count;
					}

					int Entity::Sum(string columnName)
					{
						size_t colOffset=0;
						Types colType;
						list<CD>::iterator iterCD = Columns.begin();
						list<CD>::iterator endCD = Columns.end();
						while(iterCD!=endCD)
						{
							if(iterCD->ToString()!=columnName)
							{
								colOffset += GetTypeSize(iterCD->GetType());
							}
							else{
							colType = iterCD->GetType();
							break;
							}
						}
			
						double total=0;

						list<void*>::iterator iter = Rows.begin();
						list<void*>::iterator end = Rows.end();
						while(iter!=end)
						{
							switch(colType) {
								case FLOAT:
									if(((Float*)((&iter)+colOffset))->HasValue())
									{
										total+= ((Float*)((&iter)+colOffset))->Value;
									}
									break;
								case INT:
									if(((Int*)((&iter)+colOffset))->HasValue())
									{
										total+= ((Int*)((&iter)+colOffset))->Value;
									}
									break;
								default:
									cout<<"Error, Sum only supports Int and Float"<<endl;
									return 0;
									break;
							}					
							advance(iter,1);
						}
						return (int)total; //WARNING: possible loss of percision 
					}

					void* Entity::Min(string columnName)
					{
						Cell* lowestCell=NULL;
						for each(void* row in Rows)
						{
							Cell* cell = RecoverCell(&columnName,&row,&Columns);
							if(cell->CompareTo(lowestCell)==-1){
								lowestCell = cell;
							}
						}
						return (void*)lowestCell;
					}
	
					void* Entity::Max(string columnName)
					{
						Cell* highestCell=NULL;
						for each(void* row in Rows)
						{
							Cell* cell = RecoverCell(&columnName,&row,&Columns);
							if(highestCell->CompareTo(cell)==-1){
								highestCell = cell;
							}
						}
						return (void*)highestCell;
					}
	
					string Entity::GetName() const { return Name; }
	
					string Entity::ToMetaString() const
					{
						stringstream s;
						for each(CD col in Columns)
						{
							s << col.ToString() << ",";
						}
						s << endl;
						return s.str();
					}

					string Entity::ToString() const
					{
						list<CD> cols(Columns);		//create localc copy of cols because ToString() is const and we need to access Columns.begin() and end();
						string retVal = ToMetaString();
						stringstream s;
						s << retVal;
						for each(void* r in Rows){
							void* readPointer = r;
							list<CD>::iterator iter =cols.begin();
							list<CD>::iterator end =cols.end();
							while(iter != end){
								switch(iter->GetType()) {
									case DATE:
										s << ((Date*)readPointer)->ToString() << ",";
										readPointer =  ((Date*)(readPointer))+1;
										break;
									case FLOAT:
										s << ((Float*)readPointer)->ToString() << ",";
										readPointer =  ((Float*)(readPointer))+1;
										break;
									case INT:
										s << ((Int*)readPointer)->ToString() << ",";
										readPointer =  ((Int*)(readPointer))+1;
										break;
									case TIME:
										s << ((Time*)readPointer)->ToString() << ",";
										readPointer =  ((Time*)(readPointer))+1;
										break;
									case VARCHAR:
										s << ((String*)readPointer)->ToString() << ",";
										readPointer =  ((String*)(readPointer))+1;
										break;
									default:
										throw new exception("Error during Entity::ToString() operation");
										break;
								}
							advance(iter,1);
							}
							s << endl;
						}
						retVal+=s.str();
						return retVal;
					}
				#pragma endregion 

				#pragma region Mutator Methods

					#pragma region RenameColumn

					int Entity::RenameColumn(string o, string n){		
						list<CD>::iterator iter = Columns.begin(); 
						list<CD>::iterator end =Columns.end();
						while(iter != end)
						{
							if((*iter).ToString()==o)
							{
								(*iter).Rename(n);
								return 0;	//Successful Rename
							}
							advance(iter,1);
						}
						return 1;			//Failed to find CD with name specified by 'o'
					}

					#pragma endregion
		
					#pragma region InsertByAddr

						int Entity::InsertByAddr(void* Record){
							void* readPointer = Record;
							void* newRecordAddr = (void*) operator new (sizeOfCD(Columns));
							void* insertAddr = newRecordAddr;
							list<CD>::iterator iter =Columns.begin();
							list<CD>::iterator end =Columns.end();
							while(iter != end){
								switch(iter->GetType()) {
									case DATE:
										*((Date*)insertAddr) = *((Date*)readPointer);
										insertAddr =  ((Date*)(insertAddr))+1;
										readPointer =  ((Date*)(readPointer))+1;
										break;
									case FLOAT:
										*((Float*)insertAddr) = *((Float*)readPointer);
										insertAddr =  ((Float*)(insertAddr))+1;
										readPointer =  ((Float*)(readPointer))+1;
										break;
									case INT:
										*((Int*)insertAddr) = *((Int*)readPointer);
										insertAddr =  ((Int*)(insertAddr))+1;
										readPointer =  ((Int*)(readPointer))+1;
										break;
									case TIME:
										*((Time*)insertAddr) = *((Time*)readPointer);
										insertAddr =  ((Time*)(insertAddr))+1;
										readPointer =  ((Time*)(readPointer))+1;
										break;
									case VARCHAR:
										*((String*)insertAddr) = *((String*)readPointer);
										insertAddr =  ((String*)(insertAddr))+1;
										readPointer =  ((String*)(readPointer))+1;
										break;
									default:
										throw new exception("Error during insert operation");
										return 1;
										break;
								}
								advance(iter,1);
							}
							Rows.push_back(newRecordAddr);
							return 0;	//successful insert
						}

					#pragma endregion

					#pragma region Insert

						int Entity::Insert(void* Record, ...)
						{
							va_list argList;
							va_start(argList, Record);

							void* RecordStart = (void*) operator new (sizeOfCD(Columns));
							void* insertPointer = RecordStart;
		
							list<CD>::iterator iter = Columns.begin();
							switch(iter->GetType()) {
								case DATE:
									*((Date*)RecordStart) = *((Date*)(Record));
									insertPointer =  ((Date*)(insertPointer))+1;
									break;
								case FLOAT:
									*((Float*)RecordStart) = *((Float*)(Record));
									insertPointer =  ((Float*)(insertPointer))+1;
									break;
								case INT:
									*((Int*)RecordStart) = *((Int*)(Record));
									insertPointer =  ((Int*)(insertPointer))+1;
									break;
								case TIME:
									*((Time*)RecordStart) = *((Time*)(Record));
									insertPointer =  ((Time*)(insertPointer))+1;
									break;
								case VARCHAR:
									*((String*)RecordStart) = *((String*)(Record));
									insertPointer =  ((String*)(insertPointer))+1;
									break;
								default:
									cout<<"Error in insert function"<<endl;
									return 1;
									break;
							}
							advance(iter,1);
							list<CD>::iterator end =Columns.end();
							while(iter != end){
								Date dvar(1,1,1);
								Float fvar(1);
								Int ivar(1);
								Time tvar(1,1,1);
								String svar("1");

								switch(iter->GetType()) {
								case DATE:
									dvar = va_arg(argList,Date);
									*((Date*)insertPointer) = dvar;
									insertPointer =  ((Date*)(insertPointer))+1;
									break;
								case FLOAT:
									fvar = va_arg(argList,Float);
									*((Float*)insertPointer) = fvar;
									insertPointer =  ((Float*)(insertPointer))+1;
									break;
								case INT:
									ivar = va_arg(argList,Int);
									*((Int*)insertPointer) = ivar;
									insertPointer =  ((Int*)(insertPointer))+1;
									break;
								case TIME:
									tvar = va_arg(argList,Time);
									*((Time*)insertPointer) = tvar;
									insertPointer =  ((Time*)(insertPointer))+1;
									break;
								case VARCHAR:
									svar = va_arg(argList,String);
									*((String*)insertPointer) = svar;
									insertPointer =  ((String*)(insertPointer))+1;
									break;
								default:
									cout<<"Error in insert function"<<endl;
									return 1;
									break;
								}
								advance(iter,1);
							}
							return 0;
						}
					#pragma endregion
	
					#pragma region Remove
					
					int Entity::Remove(Predicate* p){ 
						list<void*>::iterator iter = Rows.begin();
						list<void*>::iterator end = Rows.end();
						void* Row;
						while(iter != end)
						{
							Row = *iter;
							if(p->Evaluate(Row, &Columns))
							{
								list<void*>::iterator temp = iter;
								advance(temp,1);
								Rows.erase(iter);
								delete Row;
								iter = temp;
								return 0;
							}
							else{
								advance(iter,1);
							}
						}
						return 1;
					}

					#pragma endregion

					#pragma region Update
					/*
					SQL UPDATE Syntax:
						UPDATE table_name
						SET column1=value, column2=value2,...
						WHERE some_column=some_value
					*/
					//need something to set on rows which match predicate. what is CD* cd for?
					int Entity::Update(Predicate* p, CD* cd){
						list<void*>::iterator rowIt = Rows.begin();
						list<void*>::iterator end = Rows.end();

						while(rowIt != end){
							if(	p->Evaluate(&rowIt,&Columns) ){ /*update*/ }
							advance(rowIt,1);
						}

						return 1;	//Nothing was updated 
					}

					#pragma endregion

				#pragma endregion

			#pragma endregion

		#pragma endregion

		#pragma region ColumnDefinition 

			#pragma region Constructors
				
				ColumnDefinition::ColumnDefinition(const ColumnDefinition& CopyCD){
					Name = CopyCD.GetName();
					Type = CopyCD.GetType();
					Key = CopyCD.GetKey();
				}
				ColumnDefinition::ColumnDefinition(string _Name, Types _Type) {
					Name = _Name;
					Type = _Type;
					Key = false;
				}
				ColumnDefinition::ColumnDefinition(string _Name, Types _Type, bool _Key) {
					Name = _Name;
					Type = _Type;
					Key = _Key;
				}

			#pragma endregion

			void CD::Rename(string n) { Name = n; }
	
			string CD::ToString() const { return Name; }

			Types CD::GetType() const { return Type; }

			bool CD::GetKey() const { return Key; }

			string CD::GetName() const { return Name; }			

		#pragma endregion 
	#pragma endregion

}
